#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
@author: xjx
@time: 2023/10/27 11:48 
@file: driving_mode_model.py
@project: nanchangproject
@describe: 驾驶风格识别
"""
import math
import time
import sys
import django
import os
import numpy as np
from django_pandas.io import read_frame

sys.path.append('../../')
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'nanchangproject.settings')
django.setup()  # 很关键的配置，必须先setup在导入models类

from drivinginfo.models import DrivingMode, BasicInfo, Drivinginfo
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Sum, Avg, Max, Min


class DrivingModeBase(object):
    def __init__(self, car_speed_mean: float, car_speed_sd: float, acceleration_sd: float, overspeed_proportion: float,
                 rapid_acceleration_proportion: float, engine_speed_mean: float, engine_speed_sd: float,
                 driving_mode: str = ''):
        self.car_speed_mean = car_speed_mean
        self.car_speed_sd = car_speed_sd
        self.acceleration_sd = acceleration_sd
        self.overspeed_proportion = overspeed_proportion
        self.rapid_acceleration_proportion = rapid_acceleration_proportion
        self.engine_speed_mean = engine_speed_mean
        self.engine_speed_sd = engine_speed_sd
        self.driving_mode = driving_mode

    def to_list(self):
        return [self.car_speed_mean, self.car_speed_sd, self.acceleration_sd, self.overspeed_proportion,
                self.rapid_acceleration_proportion, self.engine_speed_mean, self.engine_speed_sd]


class DrivingModeInstance(DrivingModeBase):
    def __init__(self, car_speed_mean, car_speed_sd, acceleration_sd, overspeed_proportion,
                 rapid_acceleration_proportion, engine_speed_mean, engine_speed_sd, car_number, vin):
        super(DrivingModeInstance, self).__init__(car_speed_mean, car_speed_sd, acceleration_sd, overspeed_proportion,
                                                  rapid_acceleration_proportion, engine_speed_mean, engine_speed_sd, )
        self.car_number = car_number
        self.vin = vin


class DrivingModeUtil(object):
    def __init__(self):
        self.DrivingModeCluster1 = DrivingModeBase(car_speed_mean=65.59, car_speed_sd=30.21, acceleration_sd=0.15,
                                                   overspeed_proportion=0.6753,
                                                   rapid_acceleration_proportion=0.4834, engine_speed_mean=1867.94,
                                                   engine_speed_sd=520.48, driving_mode='激进型')
        self.DrivingModeCluster2 = DrivingModeBase(car_speed_mean=47.78, car_speed_sd=27.85, acceleration_sd=0.20,
                                                   overspeed_proportion=0.4689,
                                                   rapid_acceleration_proportion=0.2216, engine_speed_mean=1565.27,
                                                   engine_speed_sd=502.58, driving_mode='普通型')
        self.DrivingModeCluster3 = DrivingModeBase(car_speed_mean=31.10, car_speed_sd=19.14, acceleration_sd=0.15,
                                                   overspeed_proportion=0.2069,
                                                   rapid_acceleration_proportion=0.1161, engine_speed_mean=1360.86,
                                                   engine_speed_sd=442.27, driving_mode='保守型')

    def driving_mode_recognition(self, instance: DrivingModeInstance):
        instance_list = instance.to_list()
        cluster_list1 = self.DrivingModeCluster1.to_list()
        cluster_list2 = self.DrivingModeCluster2.to_list()
        cluster_list3 = self.DrivingModeCluster3.to_list()
        cov = np.var(np.vstack([[instance_list], [cluster_list1], [cluster_list2], [cluster_list3]]), axis=0, ddof=1)
        # 方差可能存在为0的情况，当方差为0时，应当去除这个维度（属性）的评价
        # 计算到各个聚类中心的标准化欧式距离
        print(f'方差为：{cov}')
        sum1 = 0
        sum2 = 0
        sum3 = 0
        for i in range(len(instance_list)):
            if abs(cov[i]) < 1e-6:
                continue
            sum1 += (instance_list[i] - cluster_list1[i]) ** 2 / cov[i]
            sum2 += (instance_list[i] - cluster_list2[i]) ** 2 / cov[i]
            sum3 += (instance_list[i] - cluster_list3[i]) ** 2 / cov[i]
        distance1 = math.sqrt(sum1)
        distance2 = math.sqrt(sum2)
        distance3 = math.sqrt(sum3)
        dis_list = [(distance1, self.DrivingModeCluster1.driving_mode),
                    (distance2, self.DrivingModeCluster2.driving_mode),
                    (distance3, self.DrivingModeCluster3.driving_mode)]
        dis_list = sorted(dis_list, key=lambda dis: dis[0])
        print(dis_list)
        instance.driving_mode = dis_list[0][1]

    @staticmethod
    def driving_mode_load_to_database(instance: DrivingModeInstance):
        try:
            basic_instance = BasicInfo.objects.get(vin=instance.vin)
        except ObjectDoesNotExist:
            basic_instance = BasicInfo.objects.create(
                vin=instance.vin,
                car_number=instance.car_number
            )
        DrivingMode.objects.update_or_create(
            basic_info=basic_instance,
            defaults={'basic_info': basic_instance,
                      'car_speed_mean': instance.car_speed_mean,
                      'car_speed_sd': instance.car_speed_sd,
                      'acceleration_sd': instance.acceleration_sd,
                      'overspeed_proportion': instance.overspeed_proportion,
                      'rapid_acceleration_proportion': instance.rapid_acceleration_proportion,
                      'engine_speed_mean': instance.engine_speed_mean,
                      'engine_speed_sd': instance.engine_speed_sd,
                      'driving_mode': instance.driving_mode, }
        )
        print(f'{instance.vin}车辆驾驶风格识别数据写入成功')


class StatisticalResult(object):
    def __init__(self, year, month, what_data, data):
        self.year = year
        self.month = month
        self.what_data = what_data
        self.data = data

    def __str__(self):
        return f'{self.year}年{self.month}月：{self.what_data}字段{self.data}'


def calculate_driving_mode(vin: str):
    """
    以每个月的数据计算每个月的驾驶风格
    @param vin: 该车辆VIN码
    @return: 一个StatisticalResult对象的列表
    """
    qs = Drivinginfo.objects.filter(vin=vin).order_by('data_time')  # 升序排列
    max_time = qs.aggregate(Max('data_time'))
    min_time = qs.aggregate(Min('data_time'))
    max_year = list(max_time.values())[0].year  # int类型，最近一年
    min_year = list(min_time.values())[0].year  # 最远一年
    year_iterator = min_year
    dataframe = read_frame(qs=qs)  # 将数据库对象转换为dataframe对象
    calculate_result = []
    while True:
        for month in range(1, 13):
            # 筛选当前月份的数据
            monthly_data = dataframe[
                (dataframe['data_time'].dt.month == month) & (dataframe['data_time'].dt.year == year_iterator)]
            car_speed_raw = monthly_data[monthly_data['car_speed'] != 0]
            car_speed_moving = car_speed_raw.copy()
            car_speed_moving['car_speed'] = car_speed_raw['car_speed'].astype(float)
            engine_speed_raw = monthly_data[monthly_data['engine_speed'] != 0]
            engine_data_moving = engine_speed_raw.copy()
            engine_data_moving['engine_speed'] = engine_speed_raw['engine_speed'].astype(float)
            car_speed_mean = car_speed_moving['car_speed'].mean()
            engine_speed_mean = engine_data_moving['engine_speed'].mean()
            car_speed_sd = np.std(car_speed_moving['car_speed'])
            engine_speed_sd = np.std(engine_data_moving['engine_speed'])
            total_time = len(car_speed_moving)
            # 设定大于80为高速行驶
            high_speed_time = len(car_speed_moving[car_speed_moving['car_speed'] > 80])
            # 加一保证分母不为0
            overspeed_proportion = high_speed_time / (total_time + 1)

            if not np.isnan(car_speed_mean):
                calculate_result.append(StatisticalResult(year=year_iterator,
                                                          month=month,
                                                          what_data='car_speed_mean',
                                                          data=car_speed_mean))
            if not np.isnan(car_speed_sd):
                calculate_result.append(StatisticalResult(year=year_iterator,
                                                          month=month,
                                                          what_data='car_speed_sd',
                                                          data=car_speed_sd))

            if not np.isnan(engine_speed_mean):
                calculate_result.append(StatisticalResult(year=year_iterator,
                                                          month=month,
                                                          what_data='engine_speed_mean',
                                                          data=engine_speed_mean))
            if not np.isnan(engine_speed_sd):
                calculate_result.append(StatisticalResult(year=year_iterator,
                                                          month=month,
                                                          what_data='engine_speed_sd',
                                                          data=engine_speed_sd))
            if total_time != 0:
                calculate_result.append(StatisticalResult(year=year_iterator,
                                                          month=month,
                                                          what_data='overspeed_proportion',
                                                          data=overspeed_proportion))
        year_iterator += 1
        if year_iterator > max_year:
            break
    return calculate_result


def calculate_driving_mode_all(vin: str):
    """
    以所有数据计算该车辆的驾驶风格
    @param vin: 该车辆vin码
    @return: 一个计算结果字典
    """
    # 以所有数据计算驾驶风格
    qs = Drivinginfo.objects.filter(vin=vin).order_by('data_time')  # 升序排列
    dataframe = read_frame(qs=qs)  # 将数据库对象转换为dataframe对象
    calculate_result = []
    car_speed_moving = dataframe[dataframe['car_speed'] != 0].copy()
    car_speed_moving['car_speed'] = car_speed_moving['car_speed'].astype(float)
    engine_data_moving = dataframe[dataframe['engine_speed'] != 0].copy()
    engine_data_moving['engine_speed'] = engine_data_moving['engine_speed'].astype(float)
    car_speed_mean = car_speed_moving['car_speed'].mean()
    engine_speed_mean = engine_data_moving['engine_speed'].mean()
    car_speed_sd = np.std(car_speed_moving['car_speed'])
    engine_speed_sd = np.std(engine_data_moving['engine_speed'])
    total_time_count = len(car_speed_moving)
    # 设定大于80为高速行驶
    high_speed_time_count = len(car_speed_moving[car_speed_moving['car_speed'] > 80])
    overspeed_proportion = high_speed_time_count / (total_time_count + 1)  # 加一保证分母不为0
    # 发动机转速大于2000认为是在急加速
    rapid_acceleration_proportion = len(
        engine_data_moving[engine_data_moving['engine_speed'] >= 2000]) / total_time_count
    car_speed_moving['acceleration'] = car_speed_moving['car_speed'].diff()  # 当前数据减去上一个数据
    acceleration_sd = car_speed_moving['acceleration'].std() / 36  # 求加速度标准差,1m/s=3.6km/h，同时时间间隔为10s，因此除以36
    return {'car_speed_mean': np.round(car_speed_mean, 6),
            'engine_speed_mean': np.round(engine_speed_mean, 6),
            'car_speed_sd': np.round(car_speed_sd, 6),
            'engine_speed_sd': np.round(engine_speed_sd, 6),
            'overspeed_proportion': np.round(overspeed_proportion, 6),
            'rapid_acceleration_proportion': np.round(rapid_acceleration_proportion, 6),
            'acceleration_sd': np.round(acceleration_sd, 6)}


def generate_driving_mode(vin: str):
    result = calculate_driving_mode_all(vin=vin)
    print(result)
    instance = DrivingModeInstance(
        car_speed_mean=result['car_speed_mean'],
        car_speed_sd=result['car_speed_sd'],
        acceleration_sd=result['acceleration_sd'],
        overspeed_proportion=result['overspeed_proportion'],
        rapid_acceleration_proportion=result['rapid_acceleration_proportion'],
        engine_speed_mean=result['engine_speed_mean'],
        engine_speed_sd=result['engine_speed_sd'],
        car_number='',
        vin=vin)
    x = DrivingModeUtil()
    x.driving_mode_recognition(instance)
    x.driving_mode_load_to_database(instance=instance)
    print(f'车辆{vin}驾驶风格识别成功，结果{instance.driving_mode}')


if __name__ == '__main__':
    generate_driving_mode(vin='LETYBEG27LH039366')
